from redis_connection import redis_conn as redis
from starlette.endpoints import HTTPEndpoint
from starlette.responses import JSONResponse, PlainTextResponse
import requests
import logging
import config
import json
import utils
from authenticate import AuthorizedUser
import pendulum
from database import Session
from model.ModPowerSupplyBureauOperator import PowerSupplyBureauOperator
from model.ModPowerSupplyBureauList import PowerSupplyBureauList
from model.ModPowerSupplyStationList import PowerSupplyStationList
from wx_msg_process import signature_url

logger = logging.getLogger()


def fetch_user_info(open_id):
    try:
        payload = {"access_token": redis.hget(config.PUB_ACCESS_TOKEN, "token").decode(), "openid": open_id,
                   "lang": "zh_CN"}
        req = requests.get(
            "https://api.weixin.qq.com/cgi-bin/user/info", params=payload)
        user_info = req.json()
        return user_info
    except requests.ConnectionError:
        logger.error(f"连接微信公众号服务器出现错误。")
        return None


def save_or_update_user(open_id, nickname):
    session = Session()
    try:
        exist_user = session.query(PowerSupplyBureauOperator). \
            filter(PowerSupplyBureauOperator.openid == open_id). \
            one_or_none()
        if exist_user is None:
            new_operator = PowerSupplyBureauOperator()
            new_operator.openid = open_id
            new_operator.name = nickname
            new_operator.power_supply_bureau = "00"
            session.add(new_operator)
        else:
            exist_user.name = nickname

        session.commit()
    finally:
        session.close()


def send_welcome_wagon(open_id):
    try:
        welcome_text = f"欢迎关注石家庄创新风华科技有限公司客户服务平台。您的专属服务正在开通中，请稍候。服务开通后我们会立即通知您。"
        requests.post("https://api.weixin.qq.com/cgi-bin/message/custom/send",
                      params={"access_token": redis.hget(config.PUB_ACCESS_TOKEN, "token").decode()},
                      data=json.dumps({
                          "touser": open_id,
                          "msgtype": "text",
                          "text": {
                              "content": welcome_text
                          }
                      }, ensure_ascii=False).encode("utf-8"),
                      headers={"Content-Type": "application/json; charset=utf-8"})
        logger.info(f"[WELCOME_WAGON] 向{open_id}发送欢迎词成功。")
    except requests.ConnectionError:
        logger.error(f"[WELCOME_WAGON] 发送欢迎词失败。")


class JsApiConfig(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def post(self, request):
        api_ticket = redis.hget(config.PUB_JS_API_TOKEN, "token")
        nonce_str = utils.create_nonce_str()
        timestamp = pendulum.now().int_timestamp
        post_data = await request.json()

        signature = signature_url(api_ticket, timestamp, nonce_str, post_data["req"])

        return JSONResponse({
            "appid": config.PUB_APP_ID,
            "timestamp": timestamp,
            "nonce": nonce_str,
            "signature": signature
        })


class OAuthURL(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def get(self, request) -> PlainTextResponse:
        self._logger.debug(request.query_params["redirect"])
        redirect_url = request.query_params["redirect"]
        state = "public_develop"

        return PlainTextResponse(f"https://open.weixin.qq.com/connect/oauth2/authorize?"
                                 f"appid={config.PUB_APP_ID}&redirect_uri={redirect_url}&"
                                 f"response_type=code&scope=snsapi_base&"
                                 f"state={state}#wechat_redirect")


class ExchangeAccessToken(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def get(self, request) -> JSONResponse:
        session = Session()
        try:
            request_code = request.query_params["code"]
            exchange_request = requests.get("https://api.weixin.qq.com/sns/oauth2/access_token",
                                            params={
                                                "appid": config.PUB_APP_ID,
                                                "secret": config.PUB_APP_SECRET,
                                                "code": request_code,
                                                "grant_type": "authorization_code"
                                            })
            exchange_request.raise_for_status()
            exchange_response = exchange_request.json()
            if "errcode" not in exchange_response:
                public_user = session.query(PowerSupplyBureauOperator). \
                    filter(PowerSupplyBureauOperator.openid == exchange_response["openid"]). \
                    one_or_none()
                if public_user is None:
                    raise ValueError()
                psb = session.query(PowerSupplyBureauList). \
                    filter(PowerSupplyBureauList.id == public_user.power_supply_bureau).one_or_none()
                stat = session.query(PowerSupplyStationList).filter(
                    PowerSupplyStationList.bureauid == public_user.power_supply_bureau).filter(
                    PowerSupplyStationList.stationid == public_user.stationid).one_or_none()
                authorize = AuthorizedUser(open_id=exchange_response["openid"],
                                           display_name=public_user.remark,
                                           belongs=public_user.power_supply_bureau,
                                           stationid=public_user.stationid,
                                           stationname=stat.stationname if stat is not None else '',
                                           leaders=False,
                                           area_flag=psb.area_flag if psb is not None else "",
                                           department_level=int(
                                               public_user.supervision_department)
                                           if public_user.supervision_department is not None else 0,
                                           from_enterprise=False,
                                           from_public=True)
                token = authorize.to_jwt().decode("utf-8")

                pipe = redis.pipeline()
                web_token_mark = f"{exchange_response['openid']}_web"
                if pipe.exists(web_token_mark):
                    pipe.delete(web_token_mark)
                pipe.hset(web_token_mark, "openid", exchange_response["openid"])
                pipe.hset(web_token_mark, config.PUB_WEB_TOKEN, exchange_response["access_token"])
                web_token_expires = pendulum.now().add(seconds=exchange_response["expires_in"])
                pipe.hset(web_token_mark, "web_expires", web_token_expires.to_atom_string())
                pipe.hset(web_token_mark, config.PUB_WEB_REFRESH_TOKEN, exchange_response["refresh_token"])
                refresh_expires = pendulum.now().add(days=30)
                pipe.hset(web_token_mark, "refresh_expires", refresh_expires.to_atom_string())
                pipe.setex(exchange_response["openid"], config.TOKEN_VALID_DURATION, token)
                pipe.execute()

                session.rollback()

                return JSONResponse({
                    "result": "1",
                    "token": token,
                    "expires": authorize.exp
                })
            else:
                self._logger.info(f"换取用户Access Token失败，"
                                  f"错误码{exchange_response['errcode']}，错误信息：{exchange_response['errmsg']}")
                raise ValueError()

        except (requests.ConnectionError, ValueError):
            return JSONResponse({"result": "0", "token": "", "expires": 0})
        finally:
            session.close()


class PrepareUserForLocalDebug(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def get(self, request):
        session = Session()
        try:
            if "user" not in request.query_params:
                raise ValueError()
            user_code = request.query_params["user"]
            public_user = session.query(PowerSupplyBureauOperator). \
                filter(PowerSupplyBureauOperator.openid == user_code). \
                one_or_none()
            if public_user is None:
                raise ValueError()
            psb = session.query(PowerSupplyBureauList). \
                filter(PowerSupplyBureauList.id == public_user.power_supply_bureau).one_or_none()
            stat = session.query(PowerSupplyStationList).filter(
                PowerSupplyStationList.bureauid == public_user.power_supply_bureau).filter(
                PowerSupplyStationList.stationid == public_user.stationid).one_or_none()
            authorize = AuthorizedUser(open_id=user_code,
                                       display_name=public_user.remark,
                                       belongs=public_user.power_supply_bureau,
                                       stationid=public_user.stationid,
                                       stationname=stat.stationname if stat is not None else '',
                                       leaders=False,
                                       area_flag=psb.area_flag if psb is not None else "",
                                       department_level=int(
                                           public_user.supervision_department)
                                       if public_user.supervision_department is not None else 0,
                                       from_enterprise=False,
                                       from_public=True)
            token = authorize.to_jwt().decode("utf-8")

            pipe = redis.pipeline()
            web_token_mark = f"{user_code}_web"
            if pipe.exists(web_token_mark):
                pipe.delete(web_token_mark)
            pipe.hset(web_token_mark, "openid", user_code)
            pipe.hset(web_token_mark, config.PUB_WEB_TOKEN, "N/A")
            web_token_expires = pendulum.now().add(seconds=7200)
            pipe.hset(web_token_mark, "web_expires", web_token_expires.to_atom_string())
            pipe.hset(web_token_mark, config.PUB_WEB_REFRESH_TOKEN, "N/A")
            refresh_expires = pendulum.now().add(days=30)
            pipe.hset(web_token_mark, "refresh_expires", refresh_expires.to_atom_string())
            pipe.setex(user_code, config.TOKEN_VALID_DURATION, token)
            pipe.execute()

            session.rollback()

            return JSONResponse({
                "result": "1",
                "token": token,
                "expires": authorize.exp
            })
        except (requests.ConnectionError, ValueError):
            return JSONResponse({"result": "0", "token": "", "expires": 0})
        finally:
            session.close()
